
import { ExecutiveContext, State,Executive, Autherizable, ActivityStatus, ExecuteStatus } from "../defination"
import { ActivityRecord, ByteArray, ExecutionRecord, ExecutiveRecord, StatusRecord } from "../entity"
import {  ACTIVITY, ACTIVITY_STATUS, DEALER, EXECUTE_STATUS, EXECUTION_RECORD, INPUT, LOCK_RECORD, NEXT, OUTPUT, REQUEST, RESULT, STATE_RECORD, VARIABLE } from "../shared-symbols"
import { BaseAction } from "./action"
import type { DealAction } from "./dealAction"

export const SUSPENDED = Symbol('suspended')
export class SuspendAction
<
  TActivity extends Executive<TActivity,TState,TRecord,TContext>,
  TState extends State=State,
  TRecord extends ExecutiveRecord=ExecutiveRecord,
  TContext extends ExecutiveContext=ExecutiveContext
> extends BaseAction<TActivity,TState,TRecord,TContext>
{
  [SUSPENDED]?:boolean
  constructor(act:DealAction<TActivity,TState,TRecord,TContext>){
    super(act)
  }
  
  get suspended():boolean{
    return this[SUSPENDED]===true
  }

  async invoke(context:TContext):Promise<BaseAction<TActivity,TState,TRecord,TContext>|null|undefined>{
    if (this[SUSPENDED]===true)
      throw new Error('suspend action can only be invoked once')


    const activity = this[ACTIVITY]
    const currentStatus = activity[ACTIVITY_STATUS]
    if (currentStatus !== ActivityStatus.DEALING) {
      throw new Error('only can invoke enter action when the activity is dealing')
    }
    
    const at = context.calendar.now()
    const dealerId = context[DEALER].id
    const output = this[RESULT]
    const outputBytes = output==undefined?undefined:context.serializer.serialize(output)
    const nextStatus = ActivityStatus.SUSPENDED
    const statusBytes = context.enumConverter.toByteArray<ByteArray>(nextStatus,ActivityStatus,true)!
    const finishBytes = context.enumConverter.toByteArray<ByteArray>(ExecuteStatus.FINISHED,ExecuteStatus,true)!

    activity[ACTIVITY_STATUS] = nextStatus
    activity[EXECUTE_STATUS] = ExecuteStatus.FINISHED
    activity[OUTPUT] = output

    const activityRecord = this[STATE_RECORD] as unknown as ActivityRecord
    activityRecord.activityStatus = statusBytes
    activityRecord.executeStatus = finishBytes
    activityRecord.output = outputBytes
    activityRecord.modifierId = dealerId
    activityRecord.updateTime = at
    activityRecord.executionId = undefined!

    const executionRecord = this[EXECUTION_RECORD] as ExecutionRecord
    executionRecord.activityStatus = statusBytes
    executionRecord.executeStatus = finishBytes
    executionRecord.output = outputBytes  
    executionRecord.updateTime = at
    executionRecord.overTime = at 

    await context.repository.suspendActivity(activityRecord,executionRecord,context.enumConverter.toByteArray<ByteArray>(ActivityStatus.DEALING,ActivityStatus,true)!,context[REQUEST].requestId)
    this[SUSPENDED] = true
    return this[NEXT] = this.next!
  }

  get next():undefined|null{
    if (this[SUSPENDED]===true)
      return null
    return undefined
  }
}